home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / gfx / x11 / x3270_3_2_16.lha / amiga_src / macros.c < prev    next >
C/C++ Source or Header  |  2001-06-23  |  53KB  |  2,488 lines

  1. /*
  2.  * Copyright 1993, 1994, 1995, 1996, 1999, 2000 by Paul Mattes.
  3.  *  Permission to use, copy, modify, and distribute this software and its
  4.  *  documentation for any purpose and without fee is hereby granted,
  5.  *  provided that the above copyright notice appear in all copies and that
  6.  *  both that copyright notice and this permission notice appear in
  7.  *  supporting documentation.
  8.  */
  9.  
  10. /*
  11.  *      macros.c
  12.  *              This module handles string, macro and script (sms) processing.
  13.  */
  14.  
  15. #include "globals.h"
  16.  
  17. #if defined(X3270_MENUS) /*[*/
  18. #include <X11/StringDefs.h>
  19. #include <X11/Xaw/Dialog.h>
  20. #endif /*]*/
  21.  
  22. #ifndef AMIGA
  23. #include <sys/wait.h>
  24. #include <sys/signal.h>
  25. #else
  26. #include <signal.h>
  27. #endif
  28. #include <errno.h>
  29. #include <fcntl.h>
  30. #include <signal.h>
  31. #include <stdarg.h>
  32. #include "3270ds.h"
  33. #include "appres.h"
  34. #include "ctlr.h"
  35. #include "screen.h"
  36. #include "resources.h"
  37.  
  38. #include "actionsc.h"
  39. #include "ctlrc.h"
  40. #include "ftc.h"
  41. #include "hostc.h"
  42. #include "kybdc.h"
  43. #include "macrosc.h"
  44. #include "menubarc.h"
  45. #include "popupsc.h"
  46. #if defined(X3270_PRINTER) /*[*/
  47. #include "printerc.h"
  48. #endif /*]*/
  49. #include "screenc.h"
  50. #include "statusc.h"
  51. #include "tablesc.h"
  52. #include "trace_dsc.h"
  53. #include "utilc.h"
  54. #include "xioc.h"
  55.  
  56. #define ANSI_SAVE_SIZE    4096
  57.  
  58. /* Externals */
  59. extern int      linemode;
  60.  
  61. /* Globals */
  62. struct macro_def *macro_defs = (struct macro_def *)NULL;
  63. Boolean        macro_output = False;
  64.  
  65. /* Statics */
  66. typedef struct sms {
  67.     struct sms *next;    /* next sms on the stack */
  68.     char    msc[1024];    /* input buffer */
  69.     int    msc_len;    /* length of input buffer */
  70.     char   *dptr;        /* data pointer (macros only) */
  71.     enum sms_state {
  72.         SS_IDLE,    /* no command active (scripts only) */
  73.         SS_INCOMPLETE,    /* command(s) buffered and ready to run */
  74.         SS_RUNNING,    /* command executing */
  75.         SS_KBWAIT,    /* command awaiting keyboard unlock */
  76.         SS_CONNECT_WAIT,/* command awaiting connection to complete */
  77. #if defined(X3270_FT) /*[*/
  78.         SS_FT_WAIT,    /* command awaiting file transfer to complete */
  79. #endif /*]*/
  80.         SS_PAUSED,    /* stopped in PauseScript action */
  81.         SS_WAIT_ANSI,    /* awaiting completion of Wait(ansi) */
  82.         SS_WAIT_3270,    /* awaiting completion of Wait(3270) */
  83.         SS_WAIT_OUTPUT,    /* awaiting completion of Wait(Output) */
  84.         SS_SWAIT_OUTPUT,/* awaiting completion of Snap(Wait) */
  85.         SS_WAIT_DISC,    /* awaiting completion of Wait(Disconnect) */
  86.         SS_WAIT,    /* awaiting completion of Wait() */
  87.         SS_EXPECTING,    /* awaiting completion of Expect() */
  88.         SS_CLOSING    /* awaiting completion of Close() */
  89.     } state;
  90.     enum sms_type {
  91.         ST_STRING,    /* string */
  92.         ST_MACRO,    /* macro */
  93.         ST_COMMAND,    /* interactive command */
  94.         ST_KEYMAP,    /* keyboard map */
  95.         ST_CHILD,    /* child process */
  96.         ST_PEER        /* peer (external) process */
  97.     } type;
  98.     Boolean    success;
  99.     Boolean    need_prompt;
  100.     Boolean    is_login;
  101.     Boolean    is_hex;        /* flag for ST_STRING only */
  102.     Boolean output_wait_needed;
  103.     Boolean executing;    /* recursion avoidance */
  104.     FILE   *outfile;
  105.     int    infd;
  106.     int    pid;
  107.     unsigned long expect_id;
  108.     unsigned long wait_id;
  109. } sms_t;
  110. #define SN    ((sms_t *)NULL)
  111. static sms_t *sms = SN;
  112. static int sms_depth = 0;
  113.  
  114. #if defined(X3270_TRACE) /*[*/
  115. static const char *sms_state_name[] = {
  116.     "IDLE",
  117.     "INCOMPLETE",
  118.     "RUNNING",
  119.     "KBWAIT",
  120.     "CONNECT_WAIT",
  121. #if defined(X3270_FT) /*[*/
  122.     "FT_WAIT",
  123. #endif /*]*/
  124.     "PAUSED",
  125.     "WAIT_ANSI",
  126.     "WAIT_3270",
  127.     "WAIT_OUTPUT",
  128.     "SWAIT_OUTPUT",
  129.     "WAIT_DISC",
  130.     "WAIT",
  131.     "EXPECTING",
  132.     "CLOSING"
  133. };
  134. #endif /*]*/
  135.  
  136. #if defined(X3270_MENUS) /*[*/
  137. static struct macro_def *macro_last = (struct macro_def *) NULL;
  138. #endif /*]*/
  139. static unsigned long stdin_id = 0L;
  140. static unsigned char *ansi_save_buf;
  141. static int      ansi_save_cnt = 0;
  142. static int      ansi_save_ix = 0;
  143. static char    *expect_text = CN;
  144. static int    expect_len = 0;
  145. static const char *st_name[] = { "String", "Macro", "Command", "KeymapAction",
  146.                  "ChildScript", "PeerScript" };
  147. #define ST_sNAME(s)    st_name[(int)(s)->type]
  148. #define ST_NAME        ST_sNAME(sms)
  149.  
  150. static void script_prompt(Boolean success);
  151. static void script_input(void);
  152. static void sms_pop(Boolean can_exit);
  153. static void wait_timed_out(void);
  154.  
  155. /* Macro that defines that the keyboard is locked due to user input. */
  156. #define KBWAIT    (kybdlock & (KL_OIA_LOCKED|KL_OIA_TWAIT|KL_DEFERRED_UNLOCK))
  157.  
  158. /* Macro that defines when it's safe to continue a Wait()ing sms. */
  159. #define CAN_PROCEED ( \
  160.     IN_SSCP || \
  161.     (IN_3270 && formatted && cursor_addr && !KBWAIT) || \
  162.     (IN_ANSI && !(kybdlock & KL_AWAITING_FIRST)) \
  163. )
  164.  
  165. /* Callbacks for state changes. */
  166. static void
  167. sms_connect(Boolean connected)
  168. {
  169.     /* Hack to ensure that disconnects don't cause infinite recursion. */
  170.     if (sms != SN && sms->executing)
  171.         return;
  172.  
  173.     if (!connected) {
  174.         while (sms != SN && sms->is_login) {
  175. #ifndef AMIGA
  176.             if (sms->type == ST_CHILD && sms->pid > 0)
  177.                 (void) kill(sms->pid, SIGTERM);
  178. #endif
  179.             sms_pop(False);
  180.         }
  181.     }
  182.     sms_continue();
  183. }
  184.  
  185. static void
  186. sms_in3270(Boolean in3270)
  187. {
  188.     if (in3270 || IN_SSCP)
  189.         sms_continue();
  190. }
  191.  
  192. /* One-time initialization. */
  193. void
  194. sms_init(void)
  195. {
  196.     register_schange(ST_CONNECT, sms_connect);
  197.     register_schange(ST_3270_MODE, sms_in3270);
  198. }
  199.  
  200. #if defined(X3270_MENUS) /*[*/
  201. /* Parse the macros resource into the macro list */
  202. void
  203. macros_init(void)
  204. {
  205.     char *s = CN;
  206.     char *name, *action;
  207.     struct macro_def *m;
  208.     int ns;
  209.     int ix = 1;
  210.     static char *last_s = CN;
  211.  
  212.     /* Free the previous macro definitions. */
  213.     while (macro_defs) {
  214.         m = macro_defs->next;
  215.         Free(macro_defs);
  216.         macro_defs = m;
  217.     }
  218.     macro_defs = (struct macro_def *)NULL;
  219.     macro_last = (struct macro_def *)NULL;
  220.     if (last_s) {
  221.         Free(last_s);
  222.         last_s = CN;
  223.     }
  224.  
  225.     /* Search for new ones. */
  226.     if (PCONNECTED) {
  227.         char *rname;
  228.         char *space;
  229.  
  230.         rname = NewString(current_host);
  231.         if ((space = strchr(rname, ' ')))
  232.             *space = '\0';
  233.         s = xs_buffer("%s.%s", ResMacros, rname);
  234.         Free(rname);
  235.         rname = s;
  236.         s = get_resource(rname);
  237.         Free(rname);
  238.     }
  239.     if (s == CN) {
  240.         if (appres.macros == CN)
  241.             return;
  242.         s = NewString(appres.macros);
  243.     } else
  244.         s = NewString(s);
  245.     last_s = s;
  246.  
  247.     while ((ns = split_dresource(&s, &name, &action)) == 1) {
  248.         m = (struct macro_def *)Malloc(sizeof(*m));
  249.         m->name = name;
  250.         m->action = action;
  251.         if (macro_last)
  252.             macro_last->next = m;
  253.         else
  254.             macro_defs = m;
  255.         m->next = (struct macro_def *)NULL;
  256.         macro_last = m;
  257.         ix++;
  258.     }
  259.     if (ns < 0) {
  260.         char buf[256];
  261.  
  262.         (void) sprintf(buf, "Error in macro %d", ix);
  263.         Warning(buf);
  264.     }
  265. }
  266. #endif /*]*/
  267.  
  268. /*
  269.  * Enable input from a script.
  270.  */
  271. static void
  272. script_enable(void)
  273. {
  274.     if (sms->infd >= 0 && stdin_id == 0) {
  275. #if defined(X3270_TRACE) /*[*/
  276.         if (toggled(DS_TRACE))
  277.             (void) fprintf(tracef, "Enabling input for %s[%d]\n",
  278.                 ST_NAME, sms_depth);
  279. #endif /*]*/
  280.         stdin_id = AddInput(sms->infd, script_input);
  281.     }
  282. }
  283.  
  284. /*
  285.  * Disable input from a script.
  286.  */
  287. static void
  288. script_disable(void)
  289. {
  290.     if (stdin_id != 0) {
  291. #if defined(X3270_TRACE) /*[*/
  292.         if (toggled(DS_TRACE))
  293.             (void) fprintf(tracef, "Disabling input for %s[%d]\n",
  294.                 ST_NAME, sms_depth);
  295. #endif /*]*/
  296.         RemoveInput(stdin_id);
  297.         stdin_id = 0L;
  298.     }
  299. }
  300.  
  301. /* Allocate a new sms. */
  302. static sms_t *
  303. new_sms(enum sms_type type)
  304. {
  305.     sms_t *s;
  306.  
  307.     s = (sms_t *)Calloc(1, sizeof(sms_t));
  308.  
  309.     s->state = SS_IDLE;
  310.     s->type = type;
  311.     s->dptr = s->msc;
  312.     s->success = True;
  313.     s->need_prompt = False;
  314.     s->is_login = False;
  315.     s->outfile = (FILE *)NULL;
  316.     s->infd = -1;
  317.     s->pid = -1;
  318.     s->expect_id = 0L;
  319.     s->wait_id = 0L;
  320.     s->output_wait_needed = False;
  321.     s->executing = False;
  322.  
  323.     return s;
  324. }
  325.  
  326. /*
  327.  * Push an sms definition on the stack.
  328.  * Returns whether or not that is legal.
  329.  */
  330. static Boolean
  331. sms_push(enum sms_type type)
  332. {
  333.     sms_t *s;
  334.  
  335.     /* Preempt any running sms. */
  336.     if (sms != SN) {
  337.         /* Remove the running sms's input. */
  338.         script_disable();
  339.     }
  340.  
  341.     s = new_sms(type);
  342.     if (sms != SN)
  343.         s->is_login = sms->is_login;    /* propagate from parent */
  344.     s->next = sms;
  345.     sms = s;
  346.  
  347.     /* Enable the abort button on the menu and the status indication. */
  348.     if (++sms_depth == 1) {
  349.         menubar_as_set(True);
  350.         status_script(True);
  351.     }
  352.  
  353.     if (ansi_save_buf == (unsigned char *)NULL)
  354.         ansi_save_buf = (unsigned char *)Malloc(ANSI_SAVE_SIZE);
  355.     return True;
  356. }
  357.  
  358. /*
  359.  * Add an sms definition to the _bottom_ of the stack.
  360.  */
  361. static sms_t *
  362. sms_enqueue(enum sms_type type)
  363. {
  364.     sms_t *s, *t, *t_prev = SN;
  365.  
  366.     /* Allocate and initialize a new structure. */
  367.     s = new_sms(type);
  368.  
  369.     /* Find the bottom of the stack. */
  370.     for (t = sms; t != SN; t = t->next)
  371.         t_prev = t;
  372.  
  373.     if (t_prev == SN) {    /* Empty stack. */
  374.         s->next = sms;
  375.         sms = s;
  376.  
  377.         /*
  378.          * Enable the abort button on the menu and the status
  379.          * line indication.
  380.          */
  381.         menubar_as_set(True);
  382.         status_script(True);
  383.     } else {            /* Add to bottom. */
  384.         s->next = SN;
  385.         t_prev->next = s;
  386.     }
  387.  
  388.     sms_depth++;
  389.  
  390.     if (ansi_save_buf == (unsigned char *)NULL)
  391.         ansi_save_buf = (unsigned char *)Malloc(ANSI_SAVE_SIZE);
  392.  
  393.     return s;
  394. }
  395.  
  396. /* Pop an sms definition off the stack. */
  397. static void
  398. sms_pop(Boolean can_exit)
  399. {
  400.     sms_t *s;
  401.  
  402. #if defined(X3270_TRACE) /*[*/
  403.     if (toggled(DS_TRACE))
  404.         (void) fprintf(tracef, "%s[%d] complete\n",
  405.             ST_NAME, sms_depth);
  406. #endif /*]*/
  407.  
  408.     /* When you pop the peer script, that's the end of x3270. */
  409.     if (sms->type == ST_PEER && can_exit)
  410.         x3270_exit(0);
  411.  
  412.     /* Remove the input event. */
  413.     script_disable();
  414.  
  415.     /* Close the files. */
  416.     if (sms->type == ST_CHILD) {
  417.         (void) fclose(sms->outfile);
  418.         (void) close(sms->infd);
  419.     }
  420.  
  421.     /* Cancel any pending timeouts. */
  422.     if (sms->expect_id != 0L)
  423.         RemoveTimeOut(sms->expect_id);
  424.     if (sms->wait_id != 0L)
  425.         RemoveTimeOut(sms->wait_id);
  426.  
  427.     /* Release the memory. */
  428.     s = sms;
  429.     sms = s->next;
  430.     Free((char *)s);
  431.     sms_depth--;
  432.  
  433.     if (sms == SN) {
  434.         /* Turn off the menu option. */
  435.         menubar_as_set(False);
  436.         status_script(False);
  437.     } else if (KBWAIT && (int)sms->state < (int)SS_KBWAIT) {
  438.         /* The child implicitly blocked the parent. */
  439.         sms->state = SS_KBWAIT;
  440. #if defined(X3270_TRACE) /*[*/
  441.         if (toggled(DS_TRACE))
  442.             (void) fprintf(tracef, "%s[%d] implicitly paused %s\n",
  443.                 ST_NAME, sms_depth, sms_state_name[sms->state]);
  444. #endif /*]*/
  445.     } else if (sms->state == SS_IDLE) {
  446.         /* The parent needs to be restarted. */
  447.         script_enable();
  448.     }
  449. }
  450.  
  451. /*
  452.  * Peer script initialization.
  453.  *
  454.  * Must be called after the initial call to connect to the host from the
  455.  * command line, so that the initial state can be set properly.
  456.  */
  457. void
  458. peer_script_init(void)
  459. {
  460.     sms_t *s;
  461.     Boolean on_top;
  462.  
  463.     if (!appres.scripted)
  464.         return;
  465.  
  466.     if (sms == SN) {
  467.         /* No login script running, simply push a new sms. */
  468.         (void) sms_push(ST_PEER);
  469.         s = sms;
  470.         on_top = True;
  471.     } else {
  472.         /* Login script already running, pretend we started it. */
  473.         s = sms_enqueue(ST_PEER);
  474.         s->state = SS_RUNNING;
  475.         on_top = False;
  476.     }
  477.  
  478.     s->infd = fileno(stdin);
  479.     s->outfile = stdout;
  480.     (void) SETLINEBUF(s->outfile);    /* even if it's a pipe */
  481.  
  482.     if (on_top) {
  483.         if (HALF_CONNECTED ||
  484.             (CONNECTED && (kybdlock & KL_AWAITING_FIRST)))
  485.             s->state = SS_CONNECT_WAIT;
  486.         else
  487.             script_enable();
  488.     }
  489. }
  490.  
  491.  
  492. /*
  493.  * Interpret and execute a script or macro command.
  494.  */
  495. enum em_stat { EM_CONTINUE, EM_PAUSE, EM_ERROR };
  496. static enum em_stat
  497. execute_command(enum iaction cause, char *s, char **np)
  498. {
  499.     enum {
  500.         ME_GND,        /* before action name */
  501.         ME_FUNCTION,    /* within action name */
  502.         ME_FUNCTIONx,    /* saw whitespace after action name */
  503.         ME_LPAREN,    /* saw left paren */
  504.         ME_P_PARM,    /* paren: within unquoted parameter */
  505.         ME_P_QPARM,    /* paren: within quoted parameter */
  506.         ME_P_BSL,    /* paren: after backslash in quoted parameter */
  507.         ME_P_PARMx,    /* paren: saw whitespace after parameter */
  508.         ME_S_PARM,    /* space: within unquoted parameter */
  509.         ME_S_QPARM,    /* space: within quoted parameter */
  510.         ME_S_BSL,    /* space: after backslash in quoted parameter */
  511.         ME_S_PARMx    /* space: saw whitespace after parameter */
  512.     } state = ME_GND;
  513.     char c;
  514.     char aname[64+1];
  515.     char parm[1024+1];
  516.     int nx = 0;
  517.     Cardinal count = 0;
  518.     String params[64];
  519.     int i, any, exact;
  520.     int failreason = 0;
  521.     Boolean saw_paren = False;
  522.     static const char *fail_text[] = {
  523.         /*1*/ "Action name must begin with an alphanumeric character",
  524.         /*2*/ "Syntax error in action name",
  525.         /*3*/ "Syntax error, \")\" expected",
  526.         /*4*/ "Extra data after parameters"
  527.     };
  528. #define fail(n) { failreason = n; goto failure; }
  529. #define free_params() { \
  530.     if (cause == IA_MACRO || cause == IA_KEYMAP || cause == IA_COMMAND) { \
  531.         Cardinal j; \
  532.         for (j = 0; j < count; j++) \
  533.             Free(params[j]); \
  534.     } \
  535. }
  536.  
  537.     parm[0] = '\0';
  538.     params[count] = parm;
  539.  
  540.     while ((c = *s++)) switch (state) {
  541.         case ME_GND:
  542.         if (isspace(c))
  543.             continue;
  544.         else if (isalnum(c)) {
  545.             state = ME_FUNCTION;
  546.             nx = 0;
  547.             aname[nx++] = c;
  548.         } else
  549.             fail(1);
  550.         break;
  551.         case ME_FUNCTION:    /* within function name */
  552.         if (c == '(' || isspace(c)) {
  553.             aname[nx] = '\0';
  554.             if (c == '(') {
  555.                 nx = 0;
  556.                 state = ME_LPAREN;
  557.                 saw_paren = True;
  558.             } else
  559.                 state = ME_FUNCTIONx;
  560.         } else if (isalnum(c) || c == '_' || c == '-') {
  561.             if (nx < 64)
  562.                 aname[nx++] = c;
  563.         } else {
  564.             fail(2);
  565.         }
  566.         break;
  567.         case ME_FUNCTIONx:    /* space after function name */
  568.         if (isspace(c))
  569.             continue;
  570.         else if (c == '(') {
  571.             nx = 0;
  572.             state = ME_LPAREN;
  573.         } else if (c == '"') {
  574.             nx = 0;
  575.             state = ME_S_QPARM;
  576.         }
  577.         else { state = ME_S_PARM;
  578.             nx = 0;
  579.             parm[nx++] = c;
  580.         }
  581.         break;
  582.         case ME_LPAREN:
  583.         if (isspace(c))
  584.             continue;
  585.         else if (c == '"')
  586.             state = ME_P_QPARM;
  587.         else if (c == ',') {
  588.             parm[nx++] = '\0';
  589.             params[++count] = &parm[nx];
  590.         } else if (c == ')')
  591.             goto success;
  592.         else {
  593.             state = ME_P_PARM;
  594.             parm[nx++] = c;
  595.         }
  596.         break;
  597.         case ME_P_PARM:
  598.         if (isspace(c)) {
  599.             parm[nx++] = '\0';
  600.             params[++count] = &parm[nx];
  601.             state = ME_P_PARMx;
  602.         } else if (c == ')') {
  603.             parm[nx] = '\0';
  604.             ++count;
  605.             goto success;
  606.         } else if (c == ',') {
  607.             parm[nx++] = '\0';
  608.             params[++count] = &parm[nx];
  609.             state = ME_LPAREN;
  610.         } else {
  611.             if (nx < 1024)
  612.                 parm[nx++] = c;
  613.         }
  614.         break;
  615.         case ME_P_BSL:
  616.         if (c == 'n' && nx < 1024)
  617.             parm[nx++] = '\n';
  618.         else {
  619.             if (c != '"' && nx < 1024)
  620.                 parm[nx++] = '\\';
  621.             if (nx < 1024)
  622.                 parm[nx++] = c;
  623.         }
  624.         state = ME_P_QPARM;
  625.         break;
  626.         case ME_P_QPARM:
  627.         if (c == '"') {
  628.             parm[nx++] = '\0';
  629.             params[++count] = &parm[nx];
  630.             state = ME_P_PARMx;
  631.         } else if (c == '\\') {
  632.             state = ME_P_BSL;
  633.         } else if (nx < 1024)
  634.             parm[nx++] = c;
  635.         break;
  636.         case ME_P_PARMx:
  637.         if (isspace(c))
  638.             continue;
  639.         else if (c == ',')
  640.             state = ME_LPAREN;
  641.         else if (c == ')')
  642.             goto success;
  643.         else
  644.             fail(3);
  645.         break;
  646.         case ME_S_PARM:
  647.         if (isspace(c)) {
  648.             parm[nx++] = '\0';
  649.             params[++count] = &parm[nx];
  650.             state = ME_S_PARMx;
  651.         } else {
  652.             if (nx < 1024)
  653.                 parm[nx++] = c;
  654.         }
  655.         break;
  656.         case ME_S_BSL:
  657.         if (c == 'n' && nx < 1024)
  658.             parm[nx++] = '\n';
  659.         else {
  660.             if (c != '"' && nx < 1024)
  661.                 parm[nx++] = '\\';
  662.             if (nx < 1024)
  663.                 parm[nx++] = c;
  664.         }
  665.         state = ME_S_QPARM;
  666.         break;
  667.         case ME_S_QPARM:
  668.         if (c == '"') {
  669.             parm[nx++] = '\0';
  670.             params[++count] = &parm[nx];
  671.             state = ME_S_PARMx;
  672.         } else if (c == '\\') {
  673.             state = ME_S_BSL;
  674.         } else if (nx < 1024)
  675.             parm[nx++] = c;
  676.         break;
  677.         case ME_S_PARMx:
  678.         if (isspace(c))
  679.             continue;
  680.         else if (c == '"')
  681.             state = ME_S_QPARM;
  682.         else {
  683.             parm[nx++] = c;
  684.             state = ME_S_PARM;
  685.         }
  686.         break;
  687.     }
  688.  
  689.     /* Terminal state. */
  690.     switch (state) {
  691.         case ME_FUNCTION:    /* mid-function-name */
  692.         aname[nx] = '\0';
  693.         break;
  694.         case ME_FUNCTIONx:    /* space after function */
  695.         break;
  696.         case ME_GND:    /* nothing */
  697.         if (np)
  698.             *np = s - 1;
  699.         return EM_CONTINUE;
  700.         case ME_S_PARMx:    /* space after space-style parameter */
  701.         break;
  702.         case ME_S_PARM:    /* mid space-style parameter */
  703.         parm[nx++] = '\0';
  704.         params[++count] = &parm[nx];
  705.         break;
  706.         default:
  707.         fail(3);
  708.     }
  709.  
  710.     success:
  711.     if (c) {
  712.         while (*s && isspace(*s))
  713.             s++;
  714.         if (*s) {
  715.             if (np)
  716.                 *np = s;
  717.             else
  718.                 fail(4);
  719.         } else if (np)
  720.             *np = s;
  721.     } else if (np)
  722.         *np = s-1;
  723.  
  724.     /* If it's a macro, do variable substitutions. */
  725.     if (cause == IA_MACRO || cause == IA_KEYMAP || cause == IA_COMMAND) {
  726.         Cardinal j;
  727.  
  728.         for (j = 0; j < count; j++)
  729.             params[j] = do_subst(params[j], True, False);
  730.     }
  731.  
  732.     /* Search the action list. */
  733.     if (!strncasecmp(aname, PA_PFX, strlen(PA_PFX))) {
  734.         popup_an_error("Invalid action: %s", aname);
  735.         free_params();
  736.         return EM_ERROR;
  737.     }
  738.     any = -1;
  739.     exact = -1;
  740.     for (i = 0; i < actioncount; i++) {
  741.         if (!strcasecmp(aname, actions[i].string)) {
  742.             exact = any = i;
  743.             break;
  744.         }
  745.     }
  746.     if (exact < 0) {
  747.         for (i = 0; i < actioncount; i++) {
  748.             if (!strncasecmp(aname, actions[i].string,
  749.                 strlen(aname))) {
  750.                 if (any >= 0) {
  751.                     popup_an_error("Ambiguous action name: "
  752.                         "%s", aname);
  753.                     free_params();
  754.                     return EM_ERROR;
  755.                 }
  756.                 any = i;
  757.             }
  758.         }
  759.     }
  760.     if (any >= 0) {
  761.         ia_cause = cause;
  762.         (*actions[any].proc)((Widget)NULL, (XEvent *)NULL,
  763.             count? params: (String *)NULL, &count);
  764.         free_params();
  765.         screen_disp();
  766.     } else {
  767.         popup_an_error("Unknown action: %s", aname);
  768.         free_params();
  769.         return EM_ERROR;
  770.     }
  771.  
  772. #if defined(X3270_FT) /*[*/
  773.     if (ft_state != FT_NONE)
  774.         sms->state = SS_FT_WAIT;
  775. #endif /*]*/
  776.     if (KBWAIT)
  777.         return EM_PAUSE;
  778.     else
  779.         return EM_CONTINUE;
  780.  
  781.     failure:
  782.     popup_an_error(fail_text[failreason-1]);
  783.     return EM_ERROR;
  784. #undef fail
  785. #undef free_params
  786. }
  787.  
  788. /* Run the string at the top of the stack. */
  789. static void
  790. run_string(void)
  791. {
  792.     int len;
  793.     int len_left;
  794.  
  795. #if defined(X3270_TRACE) /*[*/
  796.     if (toggled(DS_TRACE))
  797.         (void) fprintf(tracef, "%s[%d] running\n",
  798.             ST_NAME, sms_depth);
  799. #endif /*]*/
  800.  
  801.     sms->state = SS_RUNNING;
  802.     len = strlen(sms->dptr);
  803. #if defined(X3270_TRACE) /*[*/
  804.     if (toggled(DS_TRACE))
  805.         (void) fprintf(tracef, "%sString[%d]: '%s'\n",
  806.             sms->is_hex ? "Hex" : "",
  807.             sms_depth, sms->dptr);
  808. #endif /*]*/
  809.  
  810.     if (sms->is_hex) {
  811.         if (KBWAIT) {
  812.             sms->state = SS_KBWAIT;
  813. #if defined(X3270_TRACE) /*[*/
  814.             if (toggled(DS_TRACE))
  815.                 (void) fprintf(tracef,
  816.                     "%s[%d] paused %s\n",
  817.                     ST_NAME, sms_depth,
  818.                     sms_state_name[sms->state]);
  819. #endif /*]*/
  820.         } else {
  821.             hex_input(sms->dptr);
  822.             sms_pop(False);
  823.         }
  824.     } else {
  825.         if ((len_left = emulate_input(sms->dptr, len, False))) {
  826.             sms->dptr += len - len_left;
  827.             if (KBWAIT) {
  828.                 sms->state = SS_KBWAIT;
  829. #if defined(X3270_TRACE) /*[*/
  830.                 if (toggled(DS_TRACE))
  831.                     (void) fprintf(tracef,
  832.                         "%s[%d] paused %s\n",
  833.                         ST_NAME, sms_depth,
  834.                         sms_state_name[sms->state]);
  835. #endif /*]*/
  836.             }
  837.         } else {
  838.             sms_pop(False);
  839.         }
  840.     }
  841. }
  842.  
  843. /* Run the macro at the top of the stack. */
  844.  
  845. static void
  846. run_macro(void)
  847. {
  848.     char *a = sms->dptr;
  849.     char *nextm;
  850.     enum em_stat es;
  851.     sms_t *s;
  852.  
  853. #if defined(X3270_TRACE) /*[*/
  854.     if (toggled(DS_TRACE))
  855.         (void) fprintf(tracef, "%s[%d] running\n",
  856.             ST_NAME, sms_depth);
  857. #endif /*]*/
  858.  
  859.     /*
  860.      * Keep executing commands off the line until one pauses or
  861.      * we run out of commands.
  862.      */
  863.     while (*a) {
  864.         /*
  865.          * Check for command failure.
  866.          */
  867.         if (!sms->success) {
  868. #if defined(X3270_TRACE) /*[*/
  869.             if (toggled(DS_TRACE))
  870.                 (void) fprintf(tracef, "%s[%d] failed\n",
  871.                     ST_NAME, sms_depth);
  872. #endif /*]*/
  873.             /* Propogate it. */
  874.             if (sms->next != SN)
  875.                 sms->next->success = False;
  876.             break;
  877.         }
  878.  
  879.         sms->state = SS_RUNNING;
  880. #if defined(X3270_TRACE) /*[*/
  881.         if (toggled(DS_TRACE))
  882.             (void) fprintf(tracef, "%s[%d]: '%s'\n",
  883.                 ST_NAME, sms_depth, a);
  884. #endif /*]*/
  885.         s = sms;
  886.         s->success = True;
  887.         s->executing = True;
  888.         es = execute_command((s->type == ST_MACRO)? IA_MACRO:
  889.             ((s->type == ST_COMMAND)? IA_COMMAND: IA_KEYMAP),
  890.             a, &nextm);
  891.         s->executing = False;
  892.         s->dptr = nextm;
  893.  
  894.         /*
  895.          * If a new sms was started, we will be resumed
  896.          * when it completes.
  897.          */
  898.         if (sms != s) {
  899.             return;
  900.         }
  901.  
  902.         /* Macro could not execute.  Abort it. */
  903.         if (es == EM_ERROR) {
  904. #if defined(X3270_TRACE) /*[*/
  905.             if (toggled(DS_TRACE))
  906.                 (void) fprintf(tracef, "%s[%d] error\n",
  907.                     ST_NAME, sms_depth);
  908. #endif /*]*/
  909.             /* Propogate it. */
  910.             if (sms->next != SN)
  911.                 sms->next->success = False;
  912.             break;
  913.         }
  914.  
  915.         /* Macro paused, implicitly or explicitly.  Suspend it. */
  916.         if (es == EM_PAUSE || (int)sms->state >= (int)SS_KBWAIT) {
  917.             if (sms->state == SS_RUNNING)
  918.                 sms->state = SS_KBWAIT;
  919. #if defined(X3270_TRACE) /*[*/
  920.             if (toggled(DS_TRACE))
  921.                 (void) fprintf(tracef, "%s[%d] paused %s\n",
  922.                     ST_NAME, sms_depth,
  923.                     sms_state_name[sms->state]);
  924. #endif /*]*/
  925.             sms->dptr = nextm;
  926.             return;
  927.         }
  928.  
  929.         /* Macro ran. */
  930.         a = nextm;
  931.     }
  932.  
  933.     /* Finished with this macro. */
  934.     sms_pop(False);
  935. }
  936.  
  937. /* Push a macro (macro, command or keymap action) on the stack. */
  938. static void
  939. push_xmacro(enum sms_type type, char *s, Boolean is_login)
  940. {
  941.     macro_output = False;
  942.     if (!sms_push(type))
  943.         return;
  944.     (void) strncpy(sms->msc, s, 1023);
  945.     sms->msc[1023] = '\0';
  946.     sms->msc_len = strlen(s);
  947.     if (sms->msc_len > 1023)
  948.         sms->msc_len = 1023;
  949.     if (is_login) {
  950.         sms->state = SS_WAIT;
  951.         sms->is_login = True;
  952.     } else
  953.         sms->state = SS_INCOMPLETE;
  954.     if (sms_depth == 1)
  955.         sms_continue();
  956. }
  957.  
  958. /* Push a macro on the stack. */
  959. void
  960. push_macro(char *s, Boolean is_login)
  961. {
  962.     push_xmacro(ST_MACRO, s, is_login);
  963. }
  964.  
  965. /* Push an interactive command on the stack. */
  966. void
  967. push_command(char *s)
  968. {
  969.     push_xmacro(ST_COMMAND, s, False);
  970. }
  971.  
  972. /* Push an keymap action on the stack. */
  973. void
  974. push_keymap_action(char *s)
  975. {
  976.     push_xmacro(ST_KEYMAP, s, False);
  977. }
  978.  
  979. /* Push a string on the stack. */
  980. static void
  981. push_string(char *s, Boolean is_login, Boolean is_hex)
  982. {
  983.     if (!sms_push(ST_STRING))
  984.         return;
  985.     (void) strncpy(sms->msc, s, 1023);
  986.     sms->msc[1023] = '\0';
  987.     sms->msc_len = strlen(s);
  988.     if (sms->msc_len > 1023)
  989.         sms->msc_len = 1023;
  990.     if (is_login) {
  991.         sms->state = SS_WAIT;
  992.         sms->is_login = True;
  993.     } else
  994.         sms->state = SS_INCOMPLETE;
  995.     sms->is_hex = is_hex;
  996.     if (sms_depth == 1)
  997.         sms_continue();
  998. }
  999.  
  1000. /* Set a pending string. */
  1001. void
  1002. ps_set(char *s, Boolean is_hex)
  1003. {
  1004.     push_string(s, False, is_hex);
  1005. }
  1006.  
  1007. #if defined(X3270_MENUS) /*[*/
  1008. /* Callback for macros menu. */
  1009. void
  1010. macro_command(struct macro_def *m)
  1011. {
  1012.     push_macro(m->action, False);
  1013. }
  1014. #endif /*]*/
  1015.  
  1016. /*
  1017.  * If the string looks like an action, e.g., starts with "Xxx(", run a login
  1018.  * macro.  Otherwise, set a simple pending login string.
  1019.  */
  1020. void
  1021. login_macro(char *s)
  1022. {
  1023.     char *t = s;
  1024.     Boolean looks_right = False;
  1025.  
  1026.     while (isspace(*t))
  1027.         t++;
  1028.     if (isalnum(*t)) {
  1029.         while (isalnum(*t))
  1030.             t++;
  1031.         while (isspace(*t))
  1032.             t++;
  1033.         if (*t == '(')
  1034.             looks_right = True;
  1035.     }
  1036.  
  1037.     if (looks_right)
  1038.         push_macro(s, True);
  1039.     else
  1040.         push_string(s, True, False);
  1041. }
  1042.  
  1043. /* Run the first command in the msc[] buffer. */
  1044. static void
  1045. run_script(void)
  1046. {
  1047. #if defined(X3270_TRACE) /*[*/
  1048.     if (toggled(DS_TRACE))
  1049.         (void) fprintf(tracef, "%s[%d] running\n",
  1050.             ST_NAME, sms_depth);
  1051. #endif /*]*/
  1052.  
  1053.     for (;;) {
  1054.         char *ptr;
  1055.         int cmd_len;
  1056.         char *cmd;
  1057.         sms_t *s;
  1058.         enum em_stat es;
  1059.  
  1060.         /* If the script isn't idle, we're done. */
  1061.         if (sms->state != SS_IDLE)
  1062.             break;
  1063.  
  1064.         /* If a prompt is required, send one. */
  1065.         if (sms->need_prompt) {
  1066.             script_prompt(sms->success);
  1067.             sms->need_prompt = False;
  1068.         }
  1069.  
  1070.         /* If there isn't a pending command, we're done. */
  1071.         if (!sms->msc_len)
  1072.             break;
  1073.  
  1074.         /* Isolate the command. */
  1075.         ptr = memchr(sms->msc, '\n', sms->msc_len);
  1076.         if (!ptr)
  1077.             break;
  1078.         *ptr++ = '\0';
  1079.         cmd_len = ptr - sms->msc;
  1080.         cmd = sms->msc;
  1081.  
  1082.         /* Execute it. */
  1083.         sms->state = SS_RUNNING;
  1084.         sms->success = True;
  1085. #if defined(X3270_TRACE) /*[*/
  1086.         if (toggled(DS_TRACE))
  1087.             (void) fprintf(tracef, "%s[%d]: '%s'\n", ST_NAME,
  1088.                 sms_depth, cmd);
  1089. #endif /*]*/
  1090.         s = sms;
  1091.         s->executing = True;
  1092.         es = execute_command(IA_SCRIPT, cmd, (char **)NULL);
  1093.         s->executing = False;
  1094.  
  1095.         /* Move the rest of the buffer over. */
  1096.         if (cmd_len < s->msc_len) {
  1097.             s->msc_len -= cmd_len;
  1098.             (void) memmove(s->msc, ptr, s->msc_len);
  1099.         } else
  1100.             s->msc_len = 0;
  1101.  
  1102.         /*
  1103.          * If a new sms was started, we will be resumed
  1104.          * when it completes.
  1105.          */
  1106.         if (sms != s) {
  1107.             s->need_prompt = True;
  1108.             return;
  1109.         }
  1110.  
  1111.         /* Handle what it did. */
  1112.         if (es == EM_PAUSE || (int)sms->state >= (int)SS_KBWAIT) {
  1113.             if (sms->state == SS_RUNNING)
  1114.                 sms->state = SS_KBWAIT;
  1115.             script_disable();
  1116.             if (sms->state == SS_CLOSING) {
  1117.                 sms_pop(False);
  1118.                 return;
  1119.             }
  1120.             sms->need_prompt = True;
  1121.         } else if (es == EM_ERROR) {
  1122. #if defined(X3270_TRACE) /*[*/
  1123.             if (toggled(DS_TRACE))
  1124.                 (void) fprintf(tracef, "%s[%d] error\n",
  1125.                     ST_NAME, sms_depth);
  1126. #endif /*]*/
  1127.             script_prompt(False);
  1128.         } else
  1129.             script_prompt(sms->success);
  1130.         if (sms->state == SS_RUNNING)
  1131.             sms->state = SS_IDLE;
  1132.         else {
  1133. #if defined(X3270_TRACE) /*[*/
  1134.             if (toggled(DS_TRACE))
  1135.                 (void) fprintf(tracef, "%s[%d] paused %s\n",
  1136.                     ST_NAME, sms_depth,
  1137.                     sms_state_name[sms->state]);
  1138. #endif /*]*/
  1139.         }
  1140.     }
  1141. }
  1142.  
  1143. /* Handle an error generated during the execution of a script or macro. */
  1144. void
  1145. sms_error(char *msg)
  1146. {
  1147.     /* Print the error message. */
  1148.     (void) fprintf(stderr, "%s\n", msg);
  1149.  
  1150.     /* Fail the command. */
  1151.     sms->success = False;
  1152.  
  1153.     /* Cancel any login. */
  1154.     if (sms->is_login)
  1155.         host_disconnect(True);
  1156. }
  1157.  
  1158. /* Generate a response to a script command. */
  1159. void
  1160. sms_info(const char *fmt, ...)
  1161. {
  1162.     char *nl;
  1163.     char msgbuf[4096];
  1164.     char *msg = msgbuf;
  1165.     va_list args;
  1166.  
  1167.     va_start(args, fmt);
  1168.     vsprintf(msgbuf, fmt, args);
  1169.     va_end(args);
  1170.  
  1171.     do {
  1172.         int nc;
  1173.  
  1174.         nl = strchr(msg, '\n');
  1175.         if (nl != CN) {
  1176.             nc = nl - msg;
  1177.         } else
  1178.             nc = strlen(msg);
  1179.         if (nc) {
  1180.             switch (sms->type) {
  1181.             case ST_PEER:
  1182.             case ST_CHILD:
  1183.                 (void) fprintf(sms->outfile, "data: %.*s\n",
  1184.                     nc, msg);
  1185.                 break;
  1186.             default:
  1187.                 (void) printf("%.*s\n", nc, msg);
  1188.                 break;
  1189.             }
  1190.         }
  1191.         msg = nl + 1;
  1192.     } while (nl);
  1193.  
  1194.     macro_output = True;
  1195. }
  1196.  
  1197. /* Process available input from a script. */
  1198. static void
  1199. script_input(void)
  1200. {
  1201.     char buf[128];
  1202.     int nr;
  1203.     char *ptr;
  1204.     char c;
  1205.  
  1206. #if defined(X3270_TRACE) /*[*/
  1207.     if (toggled(DS_TRACE))
  1208.         (void) fprintf(tracef, "Input for %s[%d] %d\n",
  1209.             ST_NAME, sms_depth, sms->state);
  1210. #endif /*]*/
  1211.  
  1212.     /* Read in what you can. */
  1213.     nr = read(sms->infd, buf, sizeof(buf));
  1214.     if (nr < 0) {
  1215.         popup_an_errno(errno, "%s[%d] read", ST_NAME, sms_depth);
  1216.         return;
  1217.     }
  1218.     if (nr == 0) {    /* end of file */
  1219. #if defined(X3270_TRACE) /*[*/
  1220.         if (toggled(DS_TRACE))
  1221.             (void) fprintf(tracef, "EOF %s[%d]\n",
  1222.                 ST_NAME, sms_depth);
  1223. #endif /*]*/
  1224.         sms_pop(True);
  1225.         sms_continue();
  1226.         return;
  1227.     }
  1228.  
  1229.     /* Append to the pending command, ignoring returns. */
  1230.     ptr = buf;
  1231.     while (nr--)
  1232.         if ((c = *ptr++) != '\r')
  1233.             sms->msc[sms->msc_len++] = c;
  1234.  
  1235.     /* Run the command(s). */
  1236.     sms->state = SS_INCOMPLETE;
  1237.     sms_continue();
  1238. }
  1239.  
  1240. /* Resume a paused sms, if conditions are now ripe. */
  1241. void
  1242. sms_continue(void)
  1243. {
  1244.     while (True) {
  1245.         if (sms == SN)
  1246.             return;
  1247.  
  1248.         switch (sms->state) {
  1249.  
  1250.             case SS_IDLE:
  1251.             return;        /* nothing to do */
  1252.  
  1253.             case SS_INCOMPLETE:
  1254.             case SS_RUNNING:
  1255.             break;        /* let it proceed */
  1256.  
  1257.             case SS_KBWAIT:
  1258.             if (KBWAIT)
  1259.                 return;
  1260.             break;
  1261.  
  1262.             case SS_WAIT_ANSI:
  1263.             if (IN_ANSI) {
  1264.                 sms->state = SS_WAIT;
  1265.                 continue;
  1266.             }
  1267.             return;
  1268.  
  1269.             case SS_WAIT_3270:
  1270.             if (IN_3270 | IN_SSCP) {
  1271.                 sms->state = SS_WAIT;
  1272.                 continue;
  1273.             }
  1274.             return;
  1275.  
  1276.             case SS_WAIT:
  1277.             if (!CAN_PROCEED)
  1278.                 return;
  1279.             /* fall through... */
  1280.             case SS_CONNECT_WAIT:
  1281.             if (HALF_CONNECTED ||
  1282.                 (CONNECTED && (kybdlock & KL_AWAITING_FIRST)))
  1283.                 return;
  1284.             if (!CONNECTED) {
  1285.                 /* connection failed */
  1286.                 if (sms->need_prompt) {
  1287.                     script_prompt(False);
  1288.                     sms->need_prompt = False;
  1289.                 }
  1290.                 break;
  1291.             }
  1292.             break;
  1293.  
  1294. #if defined(X3270_FT) /*[*/
  1295.             case SS_FT_WAIT:
  1296.             if (ft_state == FT_NONE)
  1297.                 break;
  1298.             else
  1299.                 return;
  1300. #endif /*]*/
  1301.  
  1302.             case SS_WAIT_OUTPUT:
  1303.             case SS_SWAIT_OUTPUT:
  1304.             return;
  1305.  
  1306.             case SS_WAIT_DISC:
  1307.             if (!CONNECTED)
  1308.                 break;
  1309.             else
  1310.                 return;
  1311.  
  1312.             case SS_PAUSED:
  1313.             return;
  1314.  
  1315.             case SS_EXPECTING:
  1316.             return;
  1317.  
  1318.             case SS_CLOSING:
  1319.             return;    /* can't happen, I hope */
  1320.  
  1321.         }
  1322.  
  1323.         /* Restart the sms. */
  1324.  
  1325.         sms->state = SS_IDLE;
  1326.  
  1327.         if (sms->wait_id != 0L) {
  1328.             RemoveTimeOut(sms->wait_id);
  1329.             sms->wait_id = 0L;
  1330.         }
  1331.  
  1332.         switch (sms->type) {
  1333.             case ST_STRING:
  1334.             run_string();
  1335.             break;
  1336.             case ST_MACRO:
  1337.             case ST_COMMAND:
  1338.             case ST_KEYMAP:
  1339.             run_macro();
  1340.             break;
  1341.             case ST_PEER:
  1342.             case ST_CHILD:
  1343.             script_enable();
  1344.             run_script();
  1345.             break;
  1346.         }
  1347.     }
  1348. }
  1349.  
  1350. /*
  1351.  * Macro- and script-specific actions.
  1352.  */
  1353.  
  1354. static void
  1355. dump_range(int first, int len, Boolean in_ascii, unsigned char *buf,
  1356.     int rel_rows unused, int rel_cols)
  1357. {
  1358.     register int i;
  1359.     Boolean any = False;
  1360.     char *linebuf;
  1361.     char *s;
  1362.  
  1363.     linebuf = Malloc(maxCOLS * 3 + 1);
  1364.     s = linebuf;
  1365.  
  1366.     /*
  1367.      * If the client has looked at the live screen, then if they later
  1368.      * execute 'Wait(output)', they will need to wait for output from the
  1369.      * host.  output_wait_needed is cleared by sms_host_output,
  1370.      * which is called from the write logic in ctlr.c.
  1371.      */     
  1372.     if (sms != SN && buf == screen_buf)
  1373.         sms->output_wait_needed = True;
  1374.  
  1375.     for (i = 0; i < len; i++) {
  1376.         unsigned char c;
  1377.  
  1378.         if (i && !((first + i) % rel_cols)) {
  1379.             *s = '\0';
  1380.             action_output(linebuf);
  1381.             s = linebuf;
  1382.             any = False;
  1383.         }
  1384.         if (!any)
  1385.             any = True;
  1386.         if (in_ascii) {
  1387.             c = cg2asc[buf[first + i]];
  1388.             s += sprintf(s, "%c", c ? c : ' ');
  1389.         } else {
  1390.             s += sprintf(s, "%s%02x",
  1391.                 i ? " " : "",
  1392.                 cg2ebc[buf[first + i]]);
  1393.         }
  1394.     }
  1395.     if (any) {
  1396.         *s = '\0';
  1397.         action_output(linebuf);
  1398.     }
  1399.     Free(linebuf);
  1400. }
  1401.  
  1402. static void
  1403. dump_fixed(String params[], Cardinal count, const char *name, Boolean in_ascii,
  1404.     unsigned char *buf, int rel_rows, int rel_cols, int caddr)
  1405. {
  1406.     int row, col, len, rows = 0, cols = 0;
  1407.  
  1408.     switch (count) {
  1409.         case 0:    /* everything */
  1410.         row = 0;
  1411.         col = 0;
  1412.         len = rel_rows*rel_cols;
  1413.         break;
  1414.         case 1:    /* from cursor, for n */
  1415.         row = caddr / rel_cols;
  1416.         col = caddr % rel_cols;
  1417.         len = atoi(params[0]);
  1418.         break;
  1419.         case 3:    /* from (row,col), for n */
  1420.         row = atoi(params[0]);
  1421.         col = atoi(params[1]);
  1422.         len = atoi(params[2]);
  1423.         break;
  1424.         case 4:    /* from (row,col), for rows x cols */
  1425.         row = atoi(params[0]);
  1426.         col = atoi(params[1]);
  1427.         rows = atoi(params[2]);
  1428.         cols = atoi(params[3]);
  1429.         len = 0;
  1430.         break;
  1431.         default:
  1432.         popup_an_error("%s requires 0, 1, 3 or 4 arguments", name);
  1433.         return;
  1434.     }
  1435.  
  1436.     if (
  1437.         (row < 0 || row > rel_rows || col < 0 || col > rel_cols || len < 0) ||
  1438.         ((count < 4)  && ((row * rel_cols) + col + len > rel_rows * rel_cols)) ||
  1439.         ((count == 4) && (cols < 0 || rows < 0 ||
  1440.                   col + cols > rel_cols || row + rows > rel_rows))
  1441.        ) {
  1442.         popup_an_error("%s: Invalid argument", name);
  1443.         return;
  1444.     }
  1445.     if (count < 4)
  1446.         dump_range((row * rel_cols) + col, len, in_ascii, buf,
  1447.             rel_rows, rel_cols);
  1448.     else {
  1449.         int i;
  1450.  
  1451.         for (i = 0; i < rows; i++)
  1452.             dump_range(((row+i) * rel_cols) + col, cols, in_ascii,
  1453.                 buf, rel_rows, rel_cols);
  1454.     }
  1455. }
  1456.  
  1457. static void
  1458. dump_field(Cardinal count, const char *name, Boolean in_ascii)
  1459. {
  1460.     unsigned char *fa;
  1461.     int start, baddr;
  1462.     int len = 0;
  1463.  
  1464.     if (count != 0) {
  1465.         popup_an_error("%s requires 0 arguments", name);
  1466.         return;
  1467.     }
  1468.     if (!formatted) {
  1469.         popup_an_error("%s: Screen is not formatted", name);
  1470.         return;
  1471.     }
  1472.     fa = get_field_attribute(cursor_addr);
  1473.     start = fa - screen_buf;
  1474.     INC_BA(start);
  1475.     baddr = start;
  1476.     do {
  1477.         if (IS_FA(screen_buf[baddr]))
  1478.             break;
  1479.         len++;
  1480.         INC_BA(baddr);
  1481.     } while (baddr != start);
  1482.     dump_range(start, len, in_ascii, screen_buf, ROWS, COLS);
  1483. }
  1484.  
  1485. void
  1486. Ascii_action(Widget w unused, XEvent *event unused, String *params,
  1487.     Cardinal *num_params)
  1488. {
  1489.     dump_fixed(params, *num_params, action_name(Ascii_action), True,
  1490.         screen_buf, ROWS, COLS, cursor_addr);
  1491. }
  1492.  
  1493. void
  1494. AsciiField_action(Widget w unused, XEvent *event unused, String *params unused,
  1495.     Cardinal *num_params)
  1496. {
  1497.     dump_field(*num_params, action_name(AsciiField_action), True);
  1498. }
  1499.  
  1500. void
  1501. Ebcdic_action(Widget w unused, XEvent *event unused, String *params,
  1502.     Cardinal *num_params)
  1503. {
  1504.     dump_fixed(params, *num_params, action_name(Ebcdic_action), False,
  1505.         screen_buf, ROWS, COLS, cursor_addr);
  1506. }
  1507.  
  1508. void
  1509. EbcdicField_action(Widget w unused, XEvent *event unused,
  1510.     String *params unused, Cardinal *num_params)
  1511. {
  1512.     dump_field(*num_params, action_name(EbcdicField_action), False);
  1513. }
  1514.  
  1515. /*
  1516.  * The sms prompt is preceeded by a status line with 11 fields:
  1517.  *
  1518.  *  1 keyboard status
  1519.  *     U unlocked
  1520.  *     L locked, waiting for host response
  1521.  *     E locked, keying error
  1522.  *  2 formatting status of screen
  1523.  *     F formatted
  1524.  *     U unformatted
  1525.  *  3 protection status of current field
  1526.  *     U unprotected (modifiable)
  1527.  *     P protected
  1528.  *  4 connect status
  1529.  *     N not connected
  1530.  *     C(host) connected
  1531.  *  5 emulator mode
  1532.  *     N not connected
  1533.  *     C connected in ANSI character mode
  1534.  *     L connected in ANSI line mode
  1535.  *     P 3270 negotiation pending
  1536.  *     I connected in 3270 mode
  1537.  *  6 model number
  1538.  *  7 rows
  1539.  *  8 cols
  1540.  *  9 cursor row
  1541.  * 10 cursor col
  1542.  * 11 main window id
  1543.  */
  1544. static char *
  1545. status_string(void)
  1546. {
  1547.     char kb_stat;
  1548.     char fmt_stat;
  1549.     char prot_stat;
  1550.     char *connect_stat = CN;
  1551.     char em_mode;
  1552.     char s[1024];
  1553.     char *r;
  1554.  
  1555.     if (!kybdlock)
  1556.         kb_stat = 'U';
  1557.     else if (!CONNECTED || KBWAIT)
  1558.         kb_stat = 'L';
  1559.     else
  1560.         kb_stat = 'E';
  1561.  
  1562.     if (formatted)
  1563.         fmt_stat = 'F';
  1564.     else
  1565.         fmt_stat = 'U';
  1566.  
  1567.     if (!formatted)
  1568.         prot_stat = 'U';
  1569.     else {
  1570.         unsigned char *fa;
  1571.  
  1572.         fa = get_field_attribute(cursor_addr);
  1573.         if (FA_IS_PROTECTED(*fa))
  1574.             prot_stat = 'P';
  1575.         else
  1576.             prot_stat = 'U';
  1577.     }
  1578.  
  1579.     if (CONNECTED)
  1580.         connect_stat = xs_buffer("C(%s)", current_host);
  1581.     else
  1582.         connect_stat = NewString("N");
  1583.  
  1584.     if (CONNECTED) {
  1585.         if (IN_ANSI) {
  1586.             if (linemode)
  1587.                 em_mode = 'L';
  1588.             else
  1589.                 em_mode = 'C';
  1590.         } else if (IN_3270)
  1591.             em_mode = 'I';
  1592.         else
  1593.             em_mode = 'P';
  1594.     } else
  1595.         em_mode = 'N';
  1596.  
  1597.     (void) sprintf(s,
  1598.         "%c %c %c %s %c %d %d %d %d %d 0x%lx",
  1599.         kb_stat,
  1600.         fmt_stat,
  1601.         prot_stat,
  1602.         connect_stat,
  1603.         em_mode,
  1604.         model_num,
  1605.         ROWS, COLS,
  1606.         cursor_addr / COLS, cursor_addr % COLS,
  1607. #if defined(X3270_DISPLAY) /*[*/
  1608.         XtWindow(toplevel)
  1609. #else /*][*/
  1610.         0L
  1611. #endif /*]*/
  1612.         );
  1613.  
  1614.     r = NewString(s);
  1615.     Free(connect_stat);
  1616.     return r;
  1617. }
  1618.  
  1619. static void
  1620. script_prompt(Boolean success)
  1621. {
  1622.     char *s;
  1623.  
  1624.     s = status_string();
  1625.     (void) fprintf(sms->outfile, "%s\n%s\n", s, success ? "ok" : "error");
  1626.     (void) fflush(sms->outfile);
  1627.     Free(s);
  1628. }
  1629.  
  1630. /* Save the state of the screen for Snap queries. */
  1631. static char *snap_status = NULL;
  1632. static unsigned char *snap_buf = NULL;
  1633. static int snap_rows = 0;
  1634. static int snap_cols = 0;
  1635. static int snap_field_start = -1;
  1636. static int snap_field_length = -1;
  1637. static int snap_caddr = 0;
  1638.  
  1639. static void
  1640. snap_save(void)
  1641. {
  1642.     sms->output_wait_needed = True;
  1643.     if (snap_status != NULL)
  1644.         Free(snap_status);
  1645.     snap_status = status_string();
  1646.  
  1647.     if (snap_buf != NULL)
  1648.         Free(snap_buf);
  1649.     snap_buf = (unsigned char *)Malloc(ROWS*COLS);
  1650.     (void) memcpy(snap_buf, screen_buf, ROWS*COLS);
  1651.  
  1652.     snap_rows = ROWS;
  1653.     snap_cols = COLS;
  1654.  
  1655.     if (!formatted) {
  1656.         snap_field_start = -1;
  1657.         snap_field_length = -1;
  1658.     } else {
  1659.         unsigned char *fa;
  1660.         int baddr;
  1661.  
  1662.         snap_field_length = 0;
  1663.         fa = get_field_attribute(cursor_addr);
  1664.         snap_field_start = fa - screen_buf;
  1665.         INC_BA(snap_field_start);
  1666.         baddr = snap_field_start;
  1667.         do {
  1668.             if (IS_FA(screen_buf[baddr]))
  1669.                 break;
  1670.             snap_field_length++;
  1671.             INC_BA(baddr);
  1672.         } while (baddr != snap_field_start);
  1673.     }
  1674.     snap_caddr = cursor_addr;
  1675. }
  1676.  
  1677. /*
  1678.  * "Snap" action, maintains a snapshot for consistent multi-field comparisons:
  1679.  *
  1680.  *  Snap [Save]
  1681.  *    updates the saved image from the live image
  1682.  *  Snap Rows
  1683.  *    returns the number of rows
  1684.  *  Snap Cols
  1685.  *    returns the number of columns
  1686.  *  Snap Staus
  1687.  *  Snap Ascii ...
  1688.  *  Snap AsciiField (not yet)
  1689.  *  Snap Ebcdic ...
  1690.  *  Snap EbcdicField (not yet)
  1691.  *    runs the named command
  1692.  *  Snap Wait [tmo] Output
  1693.  *      wait for the screen to change, then do a Snap Save
  1694.  */
  1695. void
  1696. Snap_action(Widget w unused, XEvent *event unused, String *params,
  1697.     Cardinal *num_params)
  1698. {
  1699.     if (sms == SN || sms->state != SS_RUNNING) {
  1700.         popup_an_error("%s can only be called from scripts or macros",
  1701.             action_name(Snap_action));
  1702.         return;
  1703.     }
  1704.  
  1705.     if (*num_params == 0) {
  1706.         snap_save();
  1707.         return;
  1708.     }
  1709.  
  1710.     /* Handle 'Snap Wait' separately. */
  1711.     if (!strcasecmp(params[0], action_name(Wait_action))) {
  1712.         unsigned long tmo;
  1713.         char *ptr;
  1714.         int maxp = 0;
  1715.  
  1716.         if (*num_params > 1 &&
  1717.             (tmo = strtoul(params[1], &ptr, 10)) > 0 &&
  1718.             ptr != params[0] &&
  1719.             *ptr == '\0') {
  1720.             maxp = 3;
  1721.         } else {
  1722.             tmo = 0L;
  1723.             maxp = 2;
  1724.         }
  1725.         if (*num_params > maxp) {
  1726.             popup_an_error("Too many arguments to %s %s",
  1727.                 action_name(Snap_action),
  1728.                 action_name(Wait_action));
  1729.                 return;
  1730.         }
  1731.         if (*num_params < maxp) {
  1732.             popup_an_error("Too few arguments to %s %s",
  1733.                 action_name(Snap_action),
  1734.                 action_name(Wait_action));
  1735.                 return;
  1736.         }
  1737.         if (strcasecmp(params[*num_params - 1], "Output")) {
  1738.             popup_an_error("Unknown parameter to %s %s",
  1739.                 action_name(Snap_action),
  1740.                 action_name(Wait_action));
  1741.                 return;
  1742.         }
  1743.  
  1744.         /* Must be connected. */
  1745.         if (!(CONNECTED || HALF_CONNECTED)) {
  1746.             popup_an_error("%s: Not connected",
  1747.                 action_name(Snap_action));
  1748.             return;
  1749.         }
  1750.  
  1751.         /*
  1752.          * Make sure we need to wait.
  1753.          * If we don't, then Snap(Wait) is equivalent to Snap().
  1754.          */
  1755.         if (!sms->output_wait_needed) {
  1756.             snap_save();
  1757.             return;
  1758.         }
  1759.  
  1760.         /* Set the new state. */
  1761.         sms->state = SS_SWAIT_OUTPUT;
  1762.  
  1763.         /* Set up a timeout, if they want one. */
  1764.         if (tmo)
  1765.             sms->wait_id = AddTimeOut(tmo * 1000, wait_timed_out);
  1766.         return;
  1767.     }
  1768.  
  1769.     if (!strcasecmp(params[0], "Save")) {
  1770.         if (*num_params != 1) {
  1771.             popup_an_error("Extra argument(s)");
  1772.             return;
  1773.         }
  1774.         snap_save();
  1775.     } else if (!strcasecmp(params[0], "Status")) {
  1776.         if (*num_params != 1) {
  1777.             popup_an_error("Extra argument(s)");
  1778.             return;
  1779.         }
  1780.         if (snap_status == NULL) {
  1781.             popup_an_error("No saved state");
  1782.             return;
  1783.         }
  1784.         action_output("%s", snap_status);
  1785.     } else if (!strcasecmp(params[0], "Rows")) {
  1786.         if (*num_params != 1) {
  1787.             popup_an_error("Extra argument(s)");
  1788.             return;
  1789.         }
  1790.         if (snap_status == NULL) {
  1791.             popup_an_error("No saved state");
  1792.             return;
  1793.         }
  1794.         action_output("%d", snap_rows);
  1795.     } else if (!strcasecmp(params[0], "Cols")) {
  1796.         if (*num_params != 1) {
  1797.             popup_an_error("Extra argument(s)");
  1798.             return;
  1799.         }
  1800.         if (snap_status == NULL) {
  1801.             popup_an_error("No saved state");
  1802.             return;
  1803.         }
  1804.         action_output("%d", snap_cols);
  1805.     } else if (!strcasecmp(params[0], action_name(Ascii_action))) {
  1806.         if (snap_status == NULL) {
  1807.             popup_an_error("No saved state");
  1808.             return;
  1809.         }
  1810.         dump_fixed(params + 1, *num_params - 1,
  1811.             action_name(Ascii_action), True, snap_buf,
  1812.             snap_rows, snap_cols, snap_caddr);
  1813.     } else if (!strcasecmp(params[0], action_name(Ebcdic_action))) {
  1814.         if (snap_status == NULL) {
  1815.             popup_an_error("No saved state");
  1816.             return;
  1817.         }
  1818.         dump_fixed(params + 1, *num_params - 1,
  1819.             action_name(Ebcdic_action), False, snap_buf,
  1820.             snap_rows, snap_cols, snap_caddr);
  1821.     } else {
  1822.         popup_an_error("%s: Argument must be Save, Status, Rows, Cols, "
  1823.             "%s, %s or %s",
  1824.             action_name(Snap_action),
  1825.             action_name(Wait_action),
  1826.             action_name(Ascii_action),
  1827.             action_name(Ebcdic_action));
  1828.     }
  1829. }
  1830.  
  1831. /*
  1832.  * Wait for various conditions.
  1833.  */
  1834. void
  1835. Wait_action(Widget w unused, XEvent *event unused, String *params,
  1836.     Cardinal *num_params)
  1837. {
  1838.     enum sms_state next_state = SS_WAIT;
  1839.     unsigned long tmo = 0;
  1840.     char *ptr;
  1841.     Cardinal np;
  1842.     String *pr;
  1843.  
  1844.     /* Pick off the timeout parameter first. */
  1845.     if (*num_params > 0 &&
  1846.         (tmo = strtoul(params[0], &ptr, 10)) > 0 &&
  1847.         ptr != params[0] &&
  1848.         *ptr == '\0') {
  1849.         np = *num_params - 1;
  1850.         pr = params + 1;
  1851.     } else {
  1852.         np = *num_params;
  1853.         pr = params;
  1854.     }
  1855.  
  1856.     if (check_usage(Wait_action, np, 0, 1) < 0)
  1857.         return;
  1858.     if (sms == SN || sms->state != SS_RUNNING) {
  1859.         popup_an_error("%s can only be called from scripts or macros",
  1860.             action_name(Wait_action));
  1861.         return;
  1862.     }
  1863.     if (np == 1) {
  1864.         if (!strcasecmp(pr[0], "NVTMode") ||
  1865.             !strcasecmp(pr[0], "ansi")) {
  1866.             if (!IN_ANSI)
  1867.                 next_state = SS_WAIT_ANSI;
  1868.         } else if (!strcasecmp(pr[0], "3270Mode") ||
  1869.                !strcasecmp(pr[0], "3270")) {
  1870.             if (!IN_3270)
  1871.                 next_state = SS_WAIT_3270;
  1872.         } else if (!strcasecmp(pr[0], "Output")) {
  1873.             if (sms->output_wait_needed)
  1874.                 next_state = SS_WAIT_OUTPUT;
  1875.             else
  1876.                 return;
  1877.         } else if (!strcasecmp(pr[0], "Disconnect")) {
  1878.             if (CONNECTED)
  1879.                 next_state = SS_WAIT_DISC;
  1880.             else
  1881.                 return;
  1882.         } else if (strcasecmp(pr[0], "InputField")) {
  1883.             popup_an_error("%s argument must be InputField, "
  1884.                 "NVTmode, 3270Mode, Output or Disconnect",
  1885.             action_name(Wait_action));
  1886.             return;
  1887.         }
  1888.     }
  1889.     if (!(CONNECTED || HALF_CONNECTED)) {
  1890.         popup_an_error("%s: Not connected", action_name(Wait_action));
  1891.         return;
  1892.     }
  1893.  
  1894.     /* Is it already okay? */
  1895.     if (next_state == SS_WAIT && CAN_PROCEED)
  1896.         return;
  1897.  
  1898.     /* No, wait for it to happen. */
  1899.     sms->state = next_state;
  1900.  
  1901.     /* Set up a timeout, if they want one. */
  1902.     if (tmo)
  1903.         sms->wait_id = AddTimeOut(tmo * 1000, wait_timed_out);
  1904. }
  1905.  
  1906. /*
  1907.  * Callback from Connect() and Reconnect() actions, to minimally pause a
  1908.  * running sms.
  1909.  */
  1910. void
  1911. sms_connect_wait(void)
  1912. {
  1913.     if (sms != SN &&
  1914.         (int)sms->state >= (int)SS_RUNNING &&
  1915.         sms->state != SS_WAIT) {
  1916.         if (HALF_CONNECTED ||
  1917.             (CONNECTED && (kybdlock & KL_AWAITING_FIRST)))
  1918.             sms->state = SS_CONNECT_WAIT;
  1919.     }
  1920. }
  1921.  
  1922. /*
  1923.  * Callback from ctlr.c, to indicate that the host has changed the screen.
  1924.  */
  1925. void
  1926. sms_host_output(void)
  1927. {
  1928.     if (sms != SN) {
  1929.         sms->output_wait_needed = False;
  1930.  
  1931.         switch (sms->state) {
  1932.         case SS_SWAIT_OUTPUT:
  1933.             snap_save();
  1934.             /* fall through... */
  1935.         case SS_WAIT_OUTPUT:
  1936.             sms->state = SS_RUNNING;
  1937.             sms_continue();
  1938.             break;
  1939.         default:
  1940.             break;
  1941.         }
  1942.     }
  1943. }
  1944.  
  1945. /* Return whether error pop-ups should be short-circuited. */
  1946. Boolean
  1947. sms_redirect(void)
  1948. {
  1949.     return sms != SN &&
  1950.            (sms->type == ST_CHILD || sms->type == ST_PEER) &&
  1951.            (sms->state == SS_RUNNING || sms->state == SS_CONNECT_WAIT ||
  1952.         sms->wait_id != 0L);
  1953. }
  1954.  
  1955. #if defined(X3270_MENUS) || defined(C3270) /*[*/
  1956. /* Return whether any scripts are active. */
  1957. Boolean
  1958. sms_active(void)
  1959. {
  1960.     return sms != SN;
  1961. }
  1962. #endif /*]*/
  1963.  
  1964. /* Translate an expect string (uses C escape syntax). */
  1965. static void
  1966. expand_expect(char *s)
  1967. {
  1968.     char *t = Malloc(strlen(s) + 1);
  1969.     char c;
  1970.     enum { XS_BASE, XS_BS, XS_O, XS_X } state = XS_BASE;
  1971.     int n = 0;
  1972.     int nd = 0;
  1973.     static char hexes[] = "0123456789abcdef";
  1974.  
  1975.     expect_text = t;
  1976.  
  1977.     while ((c = *s++)) {
  1978.         switch (state) {
  1979.             case XS_BASE:
  1980.             if (c == '\\')
  1981.                 state = XS_BS;
  1982.             else
  1983.                 *t++ = c;
  1984.             break;
  1985.             case XS_BS:
  1986.             switch (c) {
  1987.                 case 'x':
  1988.                 nd = 0;
  1989.                 n = 0;
  1990.                 state = XS_X;
  1991.                 break;
  1992.                 case 'r':
  1993.                 *t++ = '\r';
  1994.                 state = XS_BASE;
  1995.                 break;
  1996.                 case 'n':
  1997.                 *t++ = '\n';
  1998.                 state = XS_BASE;
  1999.                 break;
  2000.                 case 'b':
  2001.                 *t++ = '\b';
  2002.                 state = XS_BASE;
  2003.                 break;
  2004.                 default:
  2005.                 if (c >= '0' && c <= '7') {
  2006.                     nd = 1;
  2007.                     n = c - '0';
  2008.                     state = XS_O;
  2009.                 } else {
  2010.                     *t++ = c;
  2011.                     state = XS_BASE;
  2012.                 }
  2013.                 break;
  2014.             }
  2015.             break;
  2016.             case XS_O:
  2017.             if (nd < 3 && c >= '0' && c <= '7') {
  2018.                 n = (n * 8) + (c - '0');
  2019.                 nd++;
  2020.             } else {
  2021.                 *t++ = n;
  2022.                 *t++ = c;
  2023.                 state = XS_BASE;
  2024.             }
  2025.             break;
  2026.             case XS_X:
  2027.             if (isxdigit(c)) {
  2028.                 n = (n * 16) +
  2029.                     strchr(hexes, tolower(c)) - hexes;
  2030.                 nd++;
  2031.             } else {
  2032.                 if (nd) 
  2033.                     *t++ = n;
  2034.                 else
  2035.                     *t++ = 'x';
  2036.                 *t++ = c;
  2037.                 state = XS_BASE;
  2038.             }
  2039.             break;
  2040.         }
  2041.     }
  2042.     expect_len = t - expect_text;
  2043. }
  2044.  
  2045. /* 'mem' version of strstr */
  2046. static char *
  2047. memstr(char *s1, char *s2, int n1, int n2)
  2048. {
  2049.     int i;
  2050.  
  2051.     for (i = 0; i <= n1 - n2; i++, s1++)
  2052.         if (*s1 == *s2 && !memcmp(s1, s2, n2))
  2053.             return s1;
  2054.     return CN;
  2055. }
  2056.  
  2057. /* Check for a match against an expect string. */
  2058. static Boolean
  2059. expect_matches(void)
  2060. {
  2061.     int ix, i;
  2062.     unsigned char buf[ANSI_SAVE_SIZE];
  2063.     char *t;
  2064.  
  2065.     ix = (ansi_save_ix + ANSI_SAVE_SIZE - ansi_save_cnt) % ANSI_SAVE_SIZE;
  2066.     for (i = 0; i < ansi_save_cnt; i++) {
  2067.         buf[i] = ansi_save_buf[(ix + i) % ANSI_SAVE_SIZE];
  2068.     }
  2069.     t = memstr((char *)buf, expect_text, ansi_save_cnt, expect_len);
  2070.     if (t != CN) {
  2071.         ansi_save_cnt -= ((unsigned char *)t - buf) + expect_len;
  2072.         Free(expect_text);
  2073.         expect_text = CN;
  2074.         return True;
  2075.     } else
  2076.         return False;
  2077. }
  2078.  
  2079. /* Store an ANSI character for use by the Ansi action. */
  2080. void
  2081. sms_store(unsigned char c)
  2082. {
  2083.     if (sms == SN)
  2084.         return;
  2085.  
  2086.     /* Save the character in the buffer. */
  2087.     ansi_save_buf[ansi_save_ix++] = c;
  2088.     ansi_save_ix %= ANSI_SAVE_SIZE;
  2089.     if (ansi_save_cnt < ANSI_SAVE_SIZE)
  2090.         ansi_save_cnt++;
  2091.  
  2092.     /* If a script or macro is waiting to match a string, check now. */
  2093.     if (sms->state == SS_EXPECTING && expect_matches()) {
  2094.         RemoveTimeOut(sms->expect_id);
  2095.         sms->expect_id = 0L;
  2096.         sms->state = SS_INCOMPLETE;
  2097.         sms_continue();
  2098.     }
  2099. }
  2100.  
  2101. /* Dump whatever ANSI data has been sent by the host since last called. */
  2102. void
  2103. AnsiText_action(Widget w unused, XEvent *event unused, String *params unused,
  2104.     Cardinal *num_params unused)
  2105. {
  2106.     register int i;
  2107.     int ix;
  2108.     unsigned char c;
  2109.     char linebuf[ANSI_SAVE_SIZE * 4 + 1];
  2110.     char *s = linebuf;
  2111.  
  2112.     if (!ansi_save_cnt)
  2113.         return;
  2114.     ix = (ansi_save_ix + ANSI_SAVE_SIZE - ansi_save_cnt) % ANSI_SAVE_SIZE;
  2115.     for (i = 0; i < ansi_save_cnt; i++) {
  2116.         c = ansi_save_buf[(ix + i) % ANSI_SAVE_SIZE];
  2117.         if (!(c & ~0x1f)) switch (c) {
  2118.             case '\n':
  2119.             s += sprintf(s, "\\n");
  2120.             break;
  2121.             case '\r':
  2122.             s += sprintf(s, "\\r");
  2123.             break;
  2124.             case '\b':
  2125.             s += sprintf(s, "\\b");
  2126.             break;
  2127.             default:
  2128.             s += sprintf(s, "\\%03o", c);
  2129.             break;
  2130.         } else if (c == '\\')
  2131.             s += sprintf(s, "\\\\");
  2132.         else
  2133.             *s++ = (char)c;
  2134.     }
  2135.     *s = '\0';
  2136.     action_output(linebuf);
  2137.     ansi_save_cnt = 0;
  2138.     ansi_save_ix = 0;
  2139. }
  2140.  
  2141. /* Pause a script. */
  2142. void
  2143. PauseScript_action(Widget w unused, XEvent *event unused, String *params unused,
  2144.     Cardinal *num_params unused)
  2145. {
  2146.     if (sms == SN || (sms->type != ST_PEER && sms->type != ST_CHILD)) {
  2147.         popup_an_error("%s can only be called from a script",
  2148.             action_name(PauseScript_action));
  2149.         return;
  2150.     }
  2151.     sms->state = SS_PAUSED;
  2152. }
  2153.  
  2154. /* Continue a script. */
  2155. void
  2156. ContinueScript_action(Widget w, XEvent *event unused, String *params,
  2157.     Cardinal *num_params)
  2158. {
  2159.     if (check_usage(ContinueScript_action, *num_params, 1, 1) < 0)
  2160.         return;
  2161.  
  2162.     /*
  2163.      * If this is a nested script, this action aborts the current script,
  2164.      * then applies to the previous one.
  2165.      */
  2166.     if (w == (Widget)NULL && sms_depth > 1)
  2167.         sms_pop(False);
  2168.  
  2169.     /* Continue the previous script. */
  2170.     if (sms == SN || sms->state != SS_PAUSED) {
  2171.         popup_an_error("%s: No script waiting",
  2172.             action_name(ContinueScript_action));
  2173.         sms_continue();
  2174.         return;
  2175.     }
  2176.     action_output("%s", params[0]);
  2177.     sms->state = SS_RUNNING;
  2178.     sms_continue();
  2179. }
  2180.  
  2181. /* Stop listening to stdin. */
  2182. void
  2183. CloseScript_action(Widget w unused, XEvent *event unused, String *params,
  2184.     Cardinal *num_params)
  2185. {
  2186.     if (sms != SN &&
  2187.         (sms->type == ST_PEER || sms->type == ST_CHILD)) {
  2188.  
  2189.         /* Close this script. */
  2190.         sms->state = SS_CLOSING;
  2191.         script_prompt(True);
  2192.  
  2193.         /* If nonzero status passed, fail the calling script. */
  2194.         if (*num_params > 0 &&
  2195.             atoi(params[0]) != 0 &&
  2196.             sms->next != SN) {
  2197.             sms->next->success = False;
  2198.             if (sms->is_login)
  2199.                 host_disconnect(True);
  2200.         }
  2201.     } else
  2202.         popup_an_error("%s can only be called from a script",
  2203.             action_name(CloseScript_action));
  2204. }
  2205.  
  2206. /* Execute an arbitrary shell command. */
  2207. void
  2208. Execute_action(Widget w unused, XEvent *event unused, String *params,
  2209.     Cardinal *num_params)
  2210. {
  2211.     if (check_usage(Execute_action, *num_params, 1, 1) < 0)
  2212.         return;
  2213.     (void) system(params[0]);
  2214. }
  2215.  
  2216. /* Timeout for Expect action. */
  2217. static void
  2218. expect_timed_out(void)
  2219. {
  2220.     if (sms == SN || sms->state != SS_EXPECTING)
  2221.         return;
  2222.  
  2223.     Free(expect_text);
  2224.     expect_text = CN;
  2225.     popup_an_error("%s: Timed out", action_name(Expect_action));
  2226.     sms->expect_id = 0L;
  2227.     sms->state = SS_INCOMPLETE;
  2228.     sms->success = False;
  2229.     if (sms->is_login)
  2230.         host_disconnect(True);
  2231.     sms_continue();
  2232. }
  2233.  
  2234. /* Timeout for Wait action. */
  2235. static void
  2236. wait_timed_out(void)
  2237. {
  2238.     /* Pop up the error message. */
  2239.     popup_an_error("%s: Timed out", action_name(Wait_action));
  2240.  
  2241.     /* Forget the ID. */
  2242.     sms->wait_id = 0L;
  2243.  
  2244.     /* If this is a login macro, it has failed. */
  2245.     if (sms->is_login)
  2246.         host_disconnect(True);
  2247.  
  2248.     sms->success = False;
  2249.     sms->state = SS_INCOMPLETE;
  2250.  
  2251.     /* Let the script proceed. */
  2252.     sms_continue();
  2253. }
  2254.  
  2255. /* Wait for a string from the host (ANSI mode only). */
  2256. void
  2257. Expect_action(Widget w unused, XEvent *event unused, String *params,
  2258.     Cardinal *num_params)
  2259. {
  2260.     int tmo;
  2261.  
  2262.     /* Verify the environment and parameters. */
  2263.     if (sms == SN || sms->state != SS_RUNNING) {
  2264.         popup_an_error("%s can only be called from a script or macro",
  2265.             action_name(Expect_action));
  2266.         return;
  2267.     }
  2268.     if (check_usage(Expect_action, *num_params, 1, 2) < 0)
  2269.         return;
  2270.     if (!IN_ANSI) {
  2271.         popup_an_error("%s is valid only when connected in ANSI mode",
  2272.             action_name(Expect_action));
  2273.     }
  2274.     if (*num_params == 2) {
  2275.         tmo = atoi(params[1]);
  2276.         if (tmo < 1 || tmo > 600) {
  2277.             popup_an_error("%s: Invalid timeout: %s",
  2278.                 action_name(Expect_action), params[1]);
  2279.             return;
  2280.         }
  2281.     } else
  2282.         tmo = 30;
  2283.  
  2284.     /* See if the text is there already; if not, wait for it. */
  2285.     expand_expect(params[0]);
  2286.     if (!expect_matches()) {
  2287.         sms->expect_id = AddTimeOut(tmo * 1000, expect_timed_out);
  2288.         sms->state = SS_EXPECTING;
  2289.     }
  2290.     /* else allow sms to proceed */
  2291. }
  2292.  
  2293.  
  2294. #if defined(X3270_MENUS) /*[*/
  2295.  
  2296. /* "Execute an Action" menu option */
  2297.  
  2298. static Widget execute_action_shell = (Widget)NULL;
  2299.  
  2300. /* Callback for "OK" button on execute action popup */
  2301. static void
  2302. execute_action_callback(Widget w unused, XtPointer client_data,
  2303.     XtPointer call_data unused)
  2304. {
  2305.     char *text;
  2306.  
  2307.     text = XawDialogGetValueString((Widget)client_data);
  2308.     XtPopdown(execute_action_shell);
  2309.     if (!text)
  2310.         return;
  2311.     push_macro(text, False);
  2312. }
  2313.  
  2314. void
  2315. execute_action_option(Widget w unused, XtPointer client_data unused,
  2316.     XtPointer call_data unused)
  2317. {
  2318.     if (execute_action_shell == NULL)
  2319.         execute_action_shell = create_form_popup("ExecuteAction",
  2320.             execute_action_callback, (XtCallbackProc)NULL, FORM_NO_CC);
  2321.  
  2322.     popup_popup(execute_action_shell, XtGrabExclusive);
  2323. }
  2324.  
  2325. #endif /*]*/
  2326.  
  2327. /* "Script" action, runs a script as a child process. */
  2328. void
  2329. Script_action(Widget w unused, XEvent *event unused, String *params,
  2330.     Cardinal *num_params)
  2331. {
  2332.     int inpipe[2];
  2333.     int outpipe[2];
  2334.  
  2335.     if (*num_params < 1) {
  2336.         popup_an_error("%s requires at least one argument",
  2337.             action_name(Script_action));
  2338.         return;
  2339.     }
  2340.  
  2341.     /* Create a new script description. */
  2342.     if (!sms_push(ST_CHILD))
  2343.         return;
  2344.  
  2345.     /*
  2346.      * Create pipes and stdout stream for the script process.
  2347.      *  inpipe[] is read by x3270, written by the script
  2348.      *  outpipe[] is written by x3270, read by the script
  2349.      */
  2350. #ifndef AMIGA
  2351.     if (pipe(inpipe) < 0) {
  2352.         sms_pop(False);
  2353.         popup_an_error("pipe() failed");
  2354.         return;
  2355.     }
  2356.     if (pipe(outpipe) < 0) {
  2357.         (void) close(inpipe[0]);
  2358.         (void) close(inpipe[1]);
  2359.         sms_pop(False);
  2360.         popup_an_error("pipe() failed");
  2361.         return;
  2362.     }
  2363. #endif
  2364.     if ((sms->outfile = fdopen(outpipe[1], "w")) == (FILE *)NULL) {
  2365.         (void) close(inpipe[0]);
  2366.         (void) close(inpipe[1]);
  2367.         (void) close(outpipe[0]);
  2368.         (void) close(outpipe[1]);
  2369.         sms_pop(False);
  2370.         popup_an_error("fdopen() failed");
  2371.         return;
  2372.     }
  2373.     (void) SETLINEBUF(sms->outfile);
  2374.  
  2375. #ifndef AMIGA
  2376.     /* Fork and exec the script process. */
  2377.     if ((sms->pid = fork()) < 0) {
  2378.         (void) close(inpipe[0]);
  2379.         (void) close(inpipe[1]);
  2380.         (void) close(outpipe[0]);
  2381.         sms_pop(False);
  2382.         popup_an_error("fork() failed");
  2383.         return;
  2384.     }
  2385. #else
  2386.         popup_an_error("fork() failed");
  2387.         return;
  2388. #endif
  2389.  
  2390.     /* Child processing. */
  2391.     if (sms->pid == 0) {
  2392.         char **argv;
  2393.         Cardinal i;
  2394.         char env_buf[2][32];
  2395.  
  2396.         /* Clean up the pipes. */
  2397.         (void) close(outpipe[1]);
  2398.         (void) close(inpipe[0]);
  2399.  
  2400.         /* Export the names of the pipes into the environment. */
  2401.         (void) sprintf(env_buf[0], "X3270OUTPUT=%d", outpipe[0]);
  2402.         (void) putenv(env_buf[0]);
  2403.         (void) sprintf(env_buf[1], "X3270INPUT=%d", inpipe[1]);
  2404.         (void) putenv(env_buf[1]);
  2405.  
  2406.         /* Set up arguments. */
  2407.         argv = (char **)Malloc((*num_params + 1) * sizeof(char *));
  2408.         for (i = 0; i < *num_params; i++)
  2409.             argv[i] = params[i];
  2410.         argv[i] = CN;
  2411.  
  2412.         /* Exec. */
  2413. #ifndef AMIGA
  2414.         (void) execvp(params[0], argv);
  2415. #endif
  2416.         (void) fprintf(stderr, "exec(%s) failed\n", params[0]);
  2417.         (void) _exit(1);
  2418.     }
  2419.  
  2420.     /* Clean up our ends of the pipes. */
  2421.     sms->infd = inpipe[0];
  2422.     (void) close(inpipe[1]);
  2423.     (void) close(outpipe[0]);
  2424.  
  2425.     /* Enable input. */
  2426.     script_enable();
  2427.  
  2428.     /* Set up to reap the child's exit status. */
  2429.     ++children;
  2430. }
  2431.  
  2432. /* "Macro" action, explicitly invokes a named macro. */
  2433. void
  2434. Macro_action(Widget w unused, XEvent *event unused, String *params,
  2435.     Cardinal *num_params)
  2436. {
  2437.     struct macro_def *m;
  2438.  
  2439.     if (check_usage(Script_action, *num_params, 1, 1) < 0)
  2440.         return;
  2441.     for (m = macro_defs; m != (struct macro_def *)NULL; m = m->next) {
  2442.         if (!strcmp(m->name, params[0])) {
  2443.             push_macro(m->action, False);
  2444.             return;
  2445.         }
  2446.     }
  2447.     popup_an_error("no such macro: '%s'", params[0]);
  2448. }
  2449.  
  2450. #if defined(X3270_PRINTER) /*[*/
  2451. /* "Printer" action, starts or stops a printer session. */
  2452. void
  2453. Printer_action(Widget w unused, XEvent *event unused, String *params,
  2454.     Cardinal *num_params)
  2455. {
  2456.     if (check_usage(Printer_action, *num_params, 1, 2) < 0)
  2457.         return;
  2458.     if (!strcasecmp(params[0], "Start")) {
  2459.         printer_start((*num_params > 1)? params[1] : CN);
  2460.     } else if (!strcasecmp(params[0], "Stop")) {
  2461.         if (*num_params != 1) {
  2462.             popup_an_error("%s: Extra argument(s)",
  2463.                 action_name(Printer_action));
  2464.             return;
  2465.         }
  2466.         printer_stop();
  2467.     } else {
  2468.         popup_an_error("%s: Argument must Start or Stop",
  2469.             action_name(Printer_action));
  2470.     }
  2471. }
  2472. #endif /*]*/
  2473.  
  2474. #if defined(X3270_MENUS) /*[*/
  2475. /* Abort all running scripts. */
  2476. void
  2477. abort_script(void)
  2478. {
  2479.     while (sms != SN) {
  2480. #ifndef AMIGA
  2481.         if (sms->type == ST_CHILD && sms->pid > 0)
  2482.             (void) kill(sms->pid, SIGTERM);
  2483. #endif
  2484.         sms_pop(True);
  2485.     }
  2486. }
  2487. #endif /*]*/
  2488.